/**
 * 
 */
package org.xnat.xnatfs.webdav;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.xnat.xnatfs.asset.AssetProvider;
import org.xnat.xnatfs.asset.IAssetProvider;
import org.xnat.xnatfs.connection.ConnectionFactory;
import org.xnat.xnatfs.connection.HttpConnection;
import org.xnat.xnatfs.connection.XnatContextProvider;
import org.xnat.xnatfs.util.PathUtils;

import com.bradmcevoy.http.Auth;
import com.bradmcevoy.http.HttpManager;
import com.bradmcevoy.http.MiltonServlet;
import com.bradmcevoy.http.Resource;
import com.bradmcevoy.http.ResourceFactory;

/**
 * Main xnatfs-webdav class
 * 
 * @author blezek
 * 
 */
public class XNATFS implements ResourceFactory {
  public enum OSType {
    WINDOWS, MACINTOSH, UNIX
  }

  private static final Logger logger = Logger.getLogger ( XNATFS.class );

  public static final String XNATFS_ASSETPROVIDER_CLASS = "xnatfs.assetprovider.class";

  LockManager lockManager;
  boolean useVerboseFolders = true;
  private boolean isInitialized = false;
  private IAssetProvider assetProvider = null;

  public XNATFS () {
    lockManager = new LockManager ();

  }

  protected void configureAssetProvider () {
    if ( assetProvider != null ) {
      return;
    }
    String clazz = MiltonServlet.servletConfig ().getInitParameter ( XNATFS_ASSETPROVIDER_CLASS );
    if ( clazz == null ) {
      clazz = AssetProvider.class.getName ();
    }
    logger.debug ( "IAssetProvider implementation: " + clazz );
    try {
      Class<?> c = Class.forName ( clazz );
      assetProvider = (IAssetProvider) c.newInstance ();
    } catch ( Throwable ex ) {
      logger.error ( "Failed to instantiate: " + clazz, ex );
    }
  }

  private VirtualResource createChild ( String path, Auth auth ) {
    if ( path == null || path.equals ( "" ) ) {
      return null;
    }
    // End the recursion
    if ( path.equals ( "/" ) ) {
      return new Root ( this, null, "/", "/" );
    }

    VirtualResource r = createChild ( PathUtils.dirname ( path ), auth );
    if ( r == null ) {
      return null;
    }
    VirtualDirectory parent = (VirtualDirectory) r;
    return parent.child ( PathUtils.tail ( path ) );
  }

  public IAssetProvider getAssetProvider () {
    return assetProvider;
  }

  public Auth getAuth () {
    Auth auth = HttpManager.request ().getAuthorization ();
    if ( auth != null ) {
      return auth;
    }
    // TODO the session auth will likely also be null due to the
    // AuthenticationFilter
    // fallback to use any auth associated with the session
    return null; // httpManager.getSessionAuthentication ( HttpManager.request () );
  }

  public LockManager getLockManager () {
    return lockManager;
  }

  private String getPathFromRequest () {
    String path = MiltonServlet.request ().getPathInfo ();
    if ( path == null ) {
      path = "/";
    }
    return path.replace ( " ", "%20" );
  }

  public Resource getResource ( String host, String ignored ) {
    initialize ();
    String path = getPathFromRequest ();
    Auth auth = getAuth ();
    logger.debug ( "getResource: " + host + " -- " + path );
    VirtualResource resource = createChild ( path, auth );
    return resource;
    /*
    if ( resource != null && resource.exists () ) {
      return resource;
    } else {
      return null;
    }
    */
  }

  // Note that supported levels of 1,2 is required for mac os write operations,
  // but might interfere with MS office editing operations
  public String getSupportedLevels () {
    return "1,2";
  }

  // Should be called when processing a request, so the context is valid.  Also configure the asset provider.
  protected void initialize () {
    if ( isInitialized ) {
      return;
    }
    XnatContextProvider p = new XnatContextProvider ();
    p.getRestURL ();
    isInitialized = true;
    configureAssetProvider ();
    InputStream inputStream = getProperties ();
    Properties props = new Properties ();
    if ( inputStream != null ) {
      logger.debug ( "Loading properties" );
      // Load the properties
      try {
        props.load ( inputStream );
      } catch ( IOException e ) {
        logger.error ( "Failed to open properties file ", e );
      }
      HttpConnection connection = new HttpConnection ();
      ConnectionFactory.setConnection ( connection );
      connection.setURL ( props.getProperty ( "url", "http://localhost:8080/xnatfs/REST/" ) );
    } else {
      logger.debug ( "Didn't find properties file XNATFS.properties" );
    }

  }

  public void setAssetProvider ( IAssetProvider assetProvider ) {
    this.assetProvider = assetProvider;
  }

  /**
   * Do we use verbose folders or not. If true, setup folders like Projects, etc.
   */
  public boolean useVerboseFolders () {
    return useVerboseFolders;
  }

  private InputStream getProperties () {
    InputStream stream = getPropertiesFile ( "/WEB-INF/XNATFS.properties" );
    // fall back and check XNAT's default location for storing configuration details
    if ( stream == null ) {
      stream = getPropertiesFile ( "/WEB-INF/conf/XNATFS.properties" );
    }
    return stream;
  }

  private InputStream getPropertiesFile ( String path ) {
    return MiltonServlet.servletConfig ().getServletContext ().getResourceAsStream ( path );
  }

}